package jef.tools.bean;

import cn.hutool.core.lang.Assert;
import org.apache.commons.lang3.ArrayUtils;

import javax.management.ReflectionException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

/**
 * @program: jef-cloud
 * @description: Bean工具类
 * @author: Cheng Zhi
 * @create: 2024-10-02 19:59
 **/
public class BeanUtils {

    static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[] {};

    /**
     * 根据类的名称和参数构造实例。
     * 如果不能创建则返回null;
     * @param className
     * @param params
     * @return
     */
    public static Object newInstance(String className, Object... params){
        Class<?> clz;
        try {
            clz = Class.forName(className);
            return newInstance(clz, params);
        } catch (ClassNotFoundException e) {
//			Logger.exeption(e);
            return null;
        } catch (ReflectionException e) {
            e.printStackTrace();
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    public static <T> T newInstance(Class<T> cls, Object... params) throws ReflectionException {
        Constructor<T> me = null;
        if (params.length == 0) {
            try {
                me = cls.getDeclaredConstructor();
                if (me != null) {
                    if (!Modifier.isPublic(me.getModifiers())) {
                        me.setAccessible(true);
                    }
                    return me.newInstance(params);
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new ReflectionException(e);
            }
        }
        List<Class<?>> list = new ArrayList<Class<?>>();
        for (Object pobj : params) {
            list.add(pobj.getClass());
        }
        Class<?>[] inputTypes = list.toArray(EMPTY_CLASS_ARRAY);
        for (Constructor<?> ct : cls.getDeclaredConstructors()) {
            if (ct.getParameterTypes().length == list.size()) {
                if (isParameterCompatible(ct.getParameterTypes(), inputTypes, ct.isVarArgs())) {
                    if (!Modifier.isPublic(ct.getModifiers())) {
                        try {
                            ct.setAccessible(true);
                        } catch (SecurityException e) {
                            System.out.println(ct.toString() + "\n" + e.getMessage());
                        }
                    }
                    try {
                        return (T) ct.newInstance(params);
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }

    /**
     * 给定方法参数类型和 值参数类型，比较值类型是否可以作为该方法的参数。
     *
     * @param methodTypes
     * @param inputTypes
     * @return
     */
    public static boolean isParameterCompatible(Class<?>[] methodTypes, Class<?>[] inputTypes, boolean varArg) {
        if (varArg && methodTypes.length == inputTypes.length - 1) {// 如果是可变参数就加上最后一个参数
            inputTypes = addElement(inputTypes, methodTypes[methodTypes.length - 1]);
        }

        if (methodTypes.length != inputTypes.length)
            return false;
        for (int i = 0; i < methodTypes.length; i++) {// 检测每一个参数的类型是否匹配
            if (inputTypes[i] == null)
                continue;
            if (methodTypes[i].isPrimitive()) { // 如果方法的参数类型 是 原生类型，则包装
                methodTypes[i] = BeanUtils.toWrapperClass(methodTypes[i]);
            }
            if (inputTypes[i].isPrimitive()) { // 如果输入参数类型 是 原生类型，则包装
                inputTypes[i] = BeanUtils.toWrapperClass(inputTypes[i]);
            }
            if (!methodTypes[i].isAssignableFrom(inputTypes[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * 将8原生类型的类转换为对应的包装的类型。
     */
    public static Class<?> toWrapperClass(Class<?> primitiveClass) {
        if (primitiveClass == Integer.TYPE)
            return Integer.class;
        if (primitiveClass == Long.TYPE)
            return Long.class;
        if (primitiveClass == Double.TYPE)
            return Double.class;
        if (primitiveClass == Short.TYPE)
            return Short.class;
        if (primitiveClass == Float.TYPE)
            return Float.class;
        if (primitiveClass == Character.TYPE)
            return Character.class;
        if (primitiveClass == Byte.TYPE)
            return Byte.class;
        if (primitiveClass == Boolean.TYPE)
            return Boolean.class;
        return primitiveClass;
    }

    @SuppressWarnings("unchecked")
    public static <T> T[] addElement(T[] array,T element) {
        if(element==null)return array;
        return addElement(array,element,(Class<T>)element.getClass());
    }

    @SuppressWarnings("unchecked")
    public static <T> T[] addAllElement(T[] array,T[] data) {
        if(data==null || data.length==0)return array;
        T[] newArray;
        if(array==null){
            return data;
        }else{
            newArray=(T[]) Array.newInstance(data.getClass().getComponentType(), array.length+data.length);
            System.arraycopy(array, 0, newArray, 0, array.length);
            System.arraycopy(data, 0, newArray, array.length, data.length);
        }
        return newArray;
    }

    /**
     * 泛型的addArray
     * apache默认的add方法居然不支持泛型...怨念...
     */
    @SuppressWarnings("unchecked")
    public static <T> T[] addElement(T[] array,T data,Class<T> componentType) {
        if(data==null)return array;
        T[] newArray;
        if(array==null){
            Assert.notNull(componentType,"The componentType shoule be assigned when the array is null.");
            newArray=(T[]) Array.newInstance(componentType, 1);
            newArray[0]=data;
        }else{
            Class<?> containerType=array.getClass().getComponentType();
            if(!containerType.isAssignableFrom(data.getClass())){//prompt the type error.
                throw new ArrayStoreException("The new element which typed "+ data.getClass().getName() +" can not be put into a array whoes type is "+ containerType.getName());
            }
            newArray=(T[])Array.newInstance(containerType, array.length+1);
            System.arraycopy(array, 0, newArray, 0, array.length);
            newArray[array.length]=data;
        }
        return newArray;
    }
}
